﻿
using PS.Framework;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Oya.Data
{
    /// <summary>
    /// Abstraction of common to XML Data Access Component
    /// </summary><remarks>the goal of this class is to have processless of XML serialization and configuration stuff </remarks>
    /// <![CDATA[
    /// Author : J. Guillaume Isabelle, 2016
    /// ]]>
    public abstract class BaseXMLDAC<T> 
    {

        public string XmlStoreTagDir { get; set; }
        internal abstract string DefineXmlStoreTagDir();
        public string XmlStoreArchiveTagDir { get; set; }
        internal abstract string DefineXmlStoreArchiveTagDir();
        public void setXmlStoreTagDirectories(string xmlStoreTagdir, string xmlStoreArchiveTagdir)
        {
            XmlStoreArchiveTagDir = xmlStoreArchiveTagdir;
            XmlStoreTagDir = xmlStoreTagdir;
        }
        // public abstract void DoInitializeXmlStoreTagDirectories();

        /// <summary>
        /// initialize XmlStoreArchiveTagDir and XmlStoreTagDir
        /// </summary>



        /// <summary>
        /// Where the XML Are stored on disk.
        /// </summary><remarks>comes from config.</remarks>
        public string Xmlstoreroot
        {
            get { return xmlstoreroot; }
            private set { xmlstoreroot = value; }
        }

        public static string xmlstoreroot = string.Empty;

        public BaseXMLDAC()
        {
            //initialize config if never did.
            if (Xmlstoreroot == string.Empty) InitConfig();
            //  DoInitializeXmlStoreTagDirectories();

            SetXmlStoreTags();
        }

        /// <summary>
        /// execute the setter for the store tag
        /// </summary>
        private void SetXmlStoreTags()
        {

            setXmlStoreTagDirectories(
                DefineXmlStoreTagDir(), DefineXmlStoreArchiveTagDir());
        }

        public BaseXMLDAC(T t, string tagdir) : this()
        {
            MakeIdString(t);
            setData(Id, tagdir);
        }

        public BaseXMLDAC(string id, string tagdir) : this()
        {
            setData(id, tagdir);
        }

        public BaseXMLDAC(string id, string tagdir, string xmlStoreTagDir, string xmlStoreArchiveTagDir) : this(id, tagdir)
        {
            setXmlStoreTagDirectories(xmlStoreTagDir, xmlStoreArchiveTagDir);
        }
        

        #region Get Data (Select)

        //public abstract T SelectFromXmlImpl(string id, string tagdir);
        public T Select(string id)
        {
            // T t = default(T);
            return CreateFromXml(id, XmlStoreTagDir);
        }
        public T SelectArchive(string id, string timestamp)
        {
            // T t = default(T);
            return CreateFromXml(id, XmlStoreArchiveTagDir);
        }

        public T Select(string id, string tagdir)
        {
            // setData(id, tagdir);

            //constructing using defaut
            // T t = default(T);

            return CreateFromXml(id, tagdir);

        }
        internal T CreateFromXml(string id, string tagdir)
        {
            setData(id, tagdir);

            try
            {
                T t = default(T);

                //use the static method to get the data
                t = CreateFromXml(TargetXmlFullPath);

                return t;
            }

            catch (Exception e)
            {
                Console.WriteLine("Failed reading from XML - " + e.Message);
                throw new XmlDACCreateFailedException(TargetXmlFullPath + " ( " + e.Message + " ) ");
            }

        }

        /// <summary>
        /// Create a response from an xml File
        /// </summary>
        /// <param name="xmlFile"></param>
        /// <returns></returns>
        internal static T CreateFromXml(string xmlFile)
        {
            T t = default(T);

            // deserializer
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            Path.GetFullPath(xmlFile);
            using (var reader = File.OpenText(xmlFile))
            {
                t = (T)serializer.Deserialize(reader);
            }

            return t;

        }


        #endregion
        #region SAVE

        public bool ArchiveData { get; set; }
        public abstract bool SetReturnDoWeArchiveData();

        //UNCLEAR IF I will need that.
        //public abstract void SaveImpl(T t, string tagdir);
        public void Save(T t)
        {
            //save data
            Save(t, XmlStoreTagDir);
            //save data if archive
            if (ArchiveData) SaveArchive(t);
        }

        /// <summary>
        /// Save an archive with dt string (hour)
        /// </summary>
        /// <param name="t"></param>
        public void SaveArchive(T t)
        {
            SaveArchive(t, FNHelper.GetDateTimeToHourString());
        }

        /// <summary>
        /// save archive by specifiying the archive subdir
        /// </summary>
        /// <param name="t"></param>
        /// <param name="archiveSubdir"></param>
        public void SaveArchive(T t, string archiveSubdir)
        {
            Save(t, XmlStoreArchiveTagDir + "/" + archiveSubdir);
        }

        public void Save(T t, string tagdir)
        {
            SetPath(t, tagdir);

            //Serializer
            System.Xml.Serialization.XmlSerializer writer =
                new System.Xml.Serialization.XmlSerializer(t.GetType());

            try
            {
                //create directory storage if not exist.
                FileInfo i = new FileInfo(TargetXmlFullPath);

                //Directory.CreateDirectory(Path.GetFullPath(TargetXmlFullPath));
                Directory.CreateDirectory(i.Directory.FullName);


                FileStream file = File.Create(TargetXmlFullPath);


                writer.Serialize(file, t);
                file.Close();

                // SaveImpl(t, tagdir);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        #endregion

        #region Prop, fields, Id 

        /// <summary>
        /// set the Id
        /// </summary>
        /// <param name="t"></param>
        internal virtual void SetId(T t)
        {
            Id = MakeIdString(t);
        }


        /// <summary>
        /// Id string used to get the data on disk.
        /// </summary>
        public string Id { get; internal set; }


        /// <summary>
        /// Implement to create the Id String with the object data
        /// </summary>
        /// <returns></returns>
        public abstract string MakeIdString(T t);


        #endregion

        #region Xml Path


        /// <summary>
        /// Set the path and Id
        /// </summary>
        /// <param name="t"></param>
        /// <param name="tagdir"></param>
        internal virtual void SetPath(T t, string tagdir)
        {
            SetId(t);
            TargetXmlFullPath = setData(Id, tagdir);

        }

        /// <summary>
        /// The xml file that we will save
        /// </summary>
        internal string TargetXmlFullPath { get; set; }

        /// <summary>
        /// where the file will be saved.
        /// </summary><remarks>made by the id and tagdir</remarks>
        private string TargetFileStoreDirectory { get; set; }




        /// <summary>
        /// Set the XML path to save the file
        /// </summary>
        /// <param name="id"></param>
        /// <param name="tagdir"></param>
        /// <returns></returns>
        internal virtual string setData(string id, string tagdir)
        {

            //Creating clean target string
            tagdir = FNHelper.CreateCleanFileName(tagdir, "-");
            Id = FNHelper.CreateCleanFileName(id, "-");

            TargetFileStoreDirectory = Xmlstoreroot + "/" + tagdir;

            return TargetFileStoreDirectory + "/" + Id + ".xml";
        }
        #endregion

        #region Static init and CTOR

        static BaseXMLDAC()
        {
            InitConfig();
        }

        private static void InitConfig()
        {
            try
            {
                xmlstoreroot = AppConfigurationDAC.Config.Xmlstoreroot;
            }
            catch (Exception e)
            {
                Console.WriteLine("Probably config not ok.");
                throw new ConfigurationErrorsException();

            }
        }

        #endregion

       
    }
    

}